<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<meta charset="utf-8"/>
		<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
		<title>Работа с файлами. Исключения</title>

		<link rel="stylesheet" type="text/css" href="http://moodle.spb.ru/file.php/84/python/standby.css"/><!-- page style: default -->
	</head>
	<body>
		<div class="page">
			<h2>Работа с файлами. Исключения</h2>
			<p>
				В заключительной части этого курса мы рассмотрим работу с файлами, используя встроенные* средства языка Python, а также познакомимся с механизмом исключений, который, является важной составляющей таких языков как C++, Java, C#, как, в прочем, и Python.
			</p>
			<div class="note">
				<p>
					Всегда помните, что, помимо встроенных функций и библиотек модулей, в интернете существует большое количество разработок, которые вы без проблем можете подключить к вашему проекту. Сторонние библиотеки могут добавлять новый функционал и расширять возможности стандартных функций и модулей, что существенно облегчает процесс разработки собственного приложения.
				</p>
			</div>
			<h3>Работа с файлами</h3>
			<p>
				Для того, чтобы начать работу с файлами, нам необходимо познакомится с ещё одним встроенным классом - типом данных <cite><b>file</b></cite>. <span class="important">Объекты этого типа поддерживают ряд методов для чтения, записи и других операций</span>. Об этих методах мы поговорим чуть позже, а сейчас давайте посмотрим, каким образом можно получить объект типа <cite>file</cite>.
			</p>
			<p>
				Для открытия внешних файлов существует специальная функция <cite>open()</cite>. В общем случае, в качестве первого и единственного аргумента нам придётся указать <i>абсолютный</i> или <i>относительный</i> путь * к нужному файлу:
				<code>
					<span style="color: #000000;">file = open('input.txt')</span>
				</code>
			</p>
			<div class="note">
				<p>
					В операционной системе Windows абсолютным считается путь, включающий в себя имя тома (диска - например, <cite>F:</cite>), перечисление всех вложенных папок и, наконец, имя файла с расширением (или без него). В Unix-совместимых ОС абсолютный путь начинается с указания корневого каталога <cite>/</cite>. Имейте в виду, что <span class="important">в Unix и Linux системах имена папок и файлов <u>регистрозависимы</u></span>, а в Windows - <u>нет</u>.
				</p>
				<p>
					Относительный путь - это путь до файла относительно текущего каталога. Если код выполняется в интерпретаторе, то текущим каталогом будет папка, в которую был установлен Python. Относительный путь может начинатся с имени файла или папки, которые должны быть в текущем каталоге. Но при этом допускается использовать символы <cite>.</cite> и <cite>..</cite> вместо имени каталога. Точка соответствует названию и месту <u>текущего</u> каталога, а две точки - каталога, находящегося на уровень <u>выше</u>. Это значит, что если текущий каталог - <cite>С:\Python</cite>, и вы пишите <cite>open('../1.txt')</cite>, то интерпретатор будет искать файл в корне диска C:, так как мы поднялись на уровень выше в дереве каталогов.
				</p>
			</div>
			<p>
				Также функция <cite>open()</cite> принимает ряд других аргументов (им заданы значения по-умолчанию), из которых наибольший интерес представляют <cite>mode</cite> (режим) и <cite>encoding</cite> (кодировка).
			</p>
			<p>
				Режим (<cite>mode</cite>) определяет режим работы с файлом. В качестве значения принимает строку, содержащую набор флагов. Как вы знаете, существуют файлы бинарные, где все данные хранятся в виде последовательностей байт, и текстовые. Помимо этого мы можем совершать две операции над файлом - чтение и запись. Так появляются различные режимы работы, которые задаются с помощью следующих флагов:
			</p>
			<table width="420px" cellpadding="0" cellspacing="0">
				<tr class="lineA head">
					<td class="left">
						Флаг
					</td>
					<td>
						Значение
					</td>
				</tr>
				<tr class="lineB">
					<td class="left">
						r
					</td>
					<td>
						Открыть для чтения
					</td>
				</tr>
				<tr class="lineA">
					<td class="left">
						w
					</td>
					<td>
						Открыть для записи
					</td>
				</tr>
				<tr class="lineB">
					<td class="left">
						a
					</td>
					<td>
						Открыть для записи в конец файла
					</td>
				</tr>
				<tr class="lineA">
					<td class="left">
						b
					</td>
					<td>
						Бинарный файл
					</td>
				</tr>
				<tr class="lineB">
					<td class="left">
						t
					</td>
					<td>
						Текстовый файл
					</td>
				</tr>
			</table>
			<p>
				По-умолчанию, если не указывать режим работы, файл откроется для чтения как текстовый. Можно комбинировать приведённые флаги - например, <cite>rb</cite> будет означать, что мы открыли бинарный файл для чтения, а <cite>at</cite> - открыли текстовый файл для записи в конец (хотя в последнем примере флаг <cite>t</cite> является необязательным).
				<code>
					<span style="color: #000000;">file = open('input.txt', mode='r', encoding='utf8')</span>
				</code>
			</p>
			<p>
				Если вы указали, что хотите открыть файл для чтения, то интерпретатор либо откроет найденный файл, либо выдаст ошибку, что файл не найдет. Если вы открываете файл для записи, интерпретатор может создать новый файл, если не найдёт его по пути, указанном в первом параметре при вызове функции.
			</p><br/>
			<p>
				После того, как мы получили файловый объект, используя его методы, можно читать информацию из него, записывать или же закрыть файл. И тут могут возникнуть проблемы. Если во время этих операций, пока файл открыт, произойдет ошибка (например, нет прав на запись), то в результате мы можем получить незакрытый в программе файл и, что ещё хуже, потерять нужные данные. Конечно, если обрабатывать <i>исключения</i> (об этом чуть позже), то это не так страшно. Однако это, все равно, создавало проблемы, поэтому разработчики Python'а добавили в язык новый оператор под названием <cite><b>with</b></cite>. <span class="important">Данный оператор позволяет не опасаться того, что файлы, с которым он работает, останутся не закрыты после завершения работы программы</span>.
			</p>
			<p>
				Синтаксис оператора следующий:
				<code>
					<span style="color: #0000FF;">with </span><span style="color: #000000;">open(аргументы) <span style="color: #0000FF;">as</span> имя [,open(аргументы) <span style="color: #0000FF;">as</span> имя] :</span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment"># code</span>
				</code>
			</p>
			<p>
				Как видите, оператор может работать сразу с несколькими файлами.
				<code>
					<span style="color: #0000FF;">with </span><span style="color: #000000;">open('input.txt') <span style="color: #0000FF;">as</span> file :</span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF;">pass</span>
				</code>
			</p>
			<p>
				Когда интерпретатор завершает выполнения блока кода оператора <cite>with</cite> или <span class="important">если внутри этого блока произошла какая-либо ошибка, он закрывает файлы, даже если мы этого явно не указали</span>. Чтобы узнать, открыт или закрыт файл, можно проверить его атрибут <cite>closed</cite> (<cite>False</cite> - открыт, <cite>True</cite> - закрыт):
				<code>
					<span style="color: #0000FF;">with </span><span style="color: #000000;">open('test.txt') <span style="color: #0000FF;">as</span> file :</span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #000000;">1 / 0</span><span class="error"> # Делить на ноль нельзя, произойдет ошибка</span><br/><br/><span style="color: #000000;">file.closed</span><span class="comment"> # True - файл закрыт, хоть мы этого не указывали явно</span>
				</code>
			</p>
			<p>
				Работать с файлами предпочтительнее именно с помощью оператора with и считается хорошей практикой.
			</p>
			</p>
				<span class="important">У файловых объектов существуют следующие <u>методы</u></span>:
			</p><br/>
			<p>
				<cite><b>read</b>([size])</cite> - читает <cite>size</cite> число символов из файла и возвращает его в виде строки. Если <cite>size</cite> не указано или отрицательно, то тогда будет считано все содержимое файла. Это будет проблемой программиста, если окажется, что файл слишком большой и не может весь поместится в памяти:
				<code>
					<span style="color: #0000FF;">with </span><span style="color: #000000;">open("test.txt") <span style="color: #0000FF;">as</span> f :</span><br/>&nbsp;&nbsp;&nbsp;&nbsp;</span>print<span style="color: #000000;">(f.read())</span>
				</code>
			</p>
			<p>
				<cite><b>readline</b>()</cite> - позволяет считывать файл построчно. Строкой считается набор символов, заканчивающихся символом перехода на новую строку <cite>\n</cite>. Если функция вернула пустую строку, это значит, что был достигнут конец файла:
				<code>
					<span style="color: #0000FF;">with </span><span style="color: #000000;">open("test.txt") <span style="color: #0000FF;">as</span> f :</span><br/>&nbsp;&nbsp;&nbsp;&nbsp;</span>print<span style="color: #000000;">(f.readline())</span><span class="comment"> # Выведет первую строку файла</span><br/>&nbsp;&nbsp;&nbsp;&nbsp;print<span style="color: #000000;">(f.readline())</span><span class="comment"> # Выведет вторую строку файла</span>
				</code>
			</p>
			<p>
				<cite><b>write</b>([строка])</cite> - записывает в открытый файл строку и возвращает количество записанных символов.
				<code>
					<span style="color: #0000FF;">with </span><span style="color: #000000;">open("out.txt", "w") <span style="color: #0000FF;">as</span> ofile :</span><br/>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">ofile.write("Hello,\n"))</span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #000000;">ofile.write("world!"))</span><span class="comment"> # Файл будет содержать две строки</span>
				</code>
			</p>
			<p>
				<cite><b>readlines</b>()</cite> - функция возвращает список всех строк из файла. Её логично использовать в цикле <cite>for</cite>, например, при копировании одного файла в другой:
				<code>
					<span style="color: #0000FF;">with </span><span style="color: #000000;">open("file1.txt") <span style="color: #0000FF;">as</span> f1,</span><span style="color: #000000;"> open("file2.txt") <span style="color: #0000FF;">as</span> f2 :</span><br/>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF;">for</span><span style="color: #000000;"> line </span><span style="color: #0000FF;">in</span><span style="color: #000000;"> f1.readlines() :</span><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #000000;">f2.write(line)</span><span class="comment"> # Построчно копируем содержимое</span>
				</code>
			</p>
			<p>
				<cite><b>close</b>()</cite> - данная функция закрывает файл, сохраняя тем самым все внесённые изменения, если они были. Использование этой функции внутри блока <cite>with-as</cite> допустимо, но <b>не</b> обязательно. Если вы не используете <cite>with</cite>, закрывать файл нужно обязательно:
				<code>
					<span style="color: #000000;">f </span><span style="color: #000000;">=</span><span style="color: #000000;"> open("test.txt")<br/></span>print<span style="color: #000000;">(f.read())<br/>f.close()</span>
				</code>
			</p>
			<h3>Исключения</h3>
			<p>
				Передавая интерпретатору на выполнение команды, вы, наверняка, сталкивались с тем, что он их не выполнял, а выводил сообщения о той или иной ошибке. <span class="important">Существуют два типа ошибок</span>: синтаксические ошибки и исключения.
			</p>
			<p>
				С синтаксическими ошибками программисты постоянно сталкиваются при написании программ. Они возникают из-за того, что были нарушены синтаксические нормы и правила написания кода. Например, для открывающей скобки <cite>(</cite> не обнаружена парная закрывающая <cite>)</cite>. При изучении Python'а люди довольно часто забывают про символ нового блока <cite>:</cite>. Например:
				<code>
					<span style="color: #0000FF;">for</span><span style="color: #000000;"> i </span><span style="color: #0000FF;">in</span><span style="color: #000000;"> range(</span><span style="color: #000000;">10</span><span style="color: #000000;">)<span class="error"> # Ошибка</span><br/>&nbsp;&nbsp;&nbsp;&nbsp;</span>print<span style="color: #000000;">(i)</span>
				</code>
			</p>
			<p>
				Если попытаться выполнить данный код, то интерпретатор выведет сообщение о синтаксической ошибке - <cite><span class="error">SyntaxError: invalid syntax</span></cite>.
			</p><br/>
			<p>
				Синтаксические ошибки не очень страшны, потому что если они есть, то интерпретатор не будет выполнять программу, а укажет на неправильный синтаксис. Куда сложнее дела обстоят с другим типом ошибок - <b>исключениями</b>. <span class="important">Даже синтаксически правильные команды при своем выполнении могут вызвать ошибку</span>. Ярчайший пример - это деление на нуль. С точки зрения синтаксиса данная команда выглядит совершенно правильно, но, тем не менее, интерпретатор не может ее выполнить, потому что он не знает, как число поделить на нуль. Другой пример: сложение разнотипных элементов. Синтаксически все тоже верно, но нельзя сложить, например, строку с числом.
			</p>
			<p>
				Итак, <span class="important" title="Определение"><b>исключения</b> - это ошибки, возникающие во время выполнения программы</span>.
			</p><br/>
			<p>
				Механизм исключений реализуется следующим образом. Интерпретатор пытается выполнить потенциально опасный участок кода, и в случае возникновения какой-либо ошибки он выполняет специально написанный для этого <i>код-обработчик</i>.
			</p>
			<p>
				Для обработки исключений существует специальная конструкция <cite><b>try-except</b></cite>, где в блок <cite>try</cite> (попробовать) помещается опасный код, а в блоке <cite>except</cite> обрабатываются всевозможные ошибки, возникающие при его выполнении:
				<code>
					<span style="color: #0000FF;">try</span><span style="color: #000000;">:<br/>&nbsp;&nbsp;&nbsp;&nbsp;[проверяемый код]<br/></span><span style="color: #0000FF;">except</span><span style="color: #000000;"> [класс ошибки]:<br/>&nbsp;&nbsp;&nbsp;&nbsp;[код</span><span style="color: #000000;">-</span><span style="color: #000000;">обработчик]</span>
    			</code>
			</p>
			<p>
				Если в блоке <cite>try</cite> возникает ошибка, то интерпретатор прерывает выполнение блока <cite>try</cite> и начинает искать блок кода для обработки возникшего исключения (ветка <cite>except</cite> у <cite>try</cite>, в котором возникла ошибка). <span class="important">Если такой блок кода не обнаружен, то интерпретатор, естественно, завершит выполнение программы и сообщит об ошибке</span>.
			</p>
			<p>
				После блока <cite>try</cite> может следовать несколько блоков except. Это необходимо, если проверяемый код может породить множество ошибок разного рода. Всевозможных классов ошибок огромное количество - деление на ноль, арифметическая ошибка, ошибка ввода-вывода - каждой соответствует свой встроенный класс. Например, деление на ноль вызывает исключение <cite><b>ZeroDivisionError</b></cite>, а ошибка ввода-вывода - <cite><b>IOError</b></cite>.
			</p>
			<div class="note">
				<p>
					<cite><b>except</b></cite> без указания класса ошибки допустимо, тогда он будет обрабатывать все возникшие исключения. Обычно этот блок помещают после всех известных обработчиков известных ошибок.
				</p>
			</div>
			<code>
				<span style="color: #0000FF;">try</span><span style="color: #000000;">:<br/>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF;">with </span><span style="color: #000000;">open("file1.txt") <span style="color: #0000FF;">as</span> f1,</span><span style="color: #000000;"> open("file2.txt") <span style="color: #0000FF;">as</span> f2 :</span><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print<span style="color: #000000;">(int(f1.read()) </span><span style="color: #000000;">/</span><span style="color: #000000;"> int(f2.read()))</span><span style="color: #000000;"><br/></span><span style="color: #0000FF;">except</span><span style="color: #000000;"> IOError:<br/>&nbsp;&nbsp;&nbsp;&nbsp;</span>print<span style="color: #000000;">('Ошибка ввода</span><span style="color: #000000;">-</span><span style="color: #000000;">вывода!')<br/></span><span style="color: #0000FF;">except</span><span style="color: #000000;"> ZeroDivisionError:<br/>&nbsp;&nbsp;&nbsp;&nbsp;</span>print<span style="color: #000000;">('Деление на нуль!')<br/></span><span style="color: #0000FF;">except</span><span style="color: #000000;"> KeyboardInterrupt:<br/>&nbsp;&nbsp;&nbsp;&nbsp;</span>print<span style="color: #000000;">('Прерывание с клавиатуры')<br/></span><span style="color: #0000FF;">except</span><span style="color: #000000;">:<br/>&nbsp;&nbsp;&nbsp;&nbsp;</span>print<span style="color: #000000;">('Неизвестная ошибка!')</span>
			</code>
			<p>
				В этом примере берутся числа из двух файлов и делятся одно на другое. При выполнении этого кода может возникнуть несколько исключительных ситуаций. Во-первых, файла или файлов может не существовать, тогда функция open вызовет ошибку. Во-вторых, к файлам доступ может быть закрыт, что также вызовет ошибку чтения. В-третьих, функция чтения и функция приведения типа <cite>int()</cite> могут создать свои собственные ошибки. Ну и напоследок, мы имеем все шансы получить при выполнении деление на <cite>0</cite>.
			</p>
			<p>
				Как видите, ошибок может возникнуть масса. Как же их всех обработать? Обычно поступают следующим образом: уникальные ошибки отлавливают с помощью классов ошибок, а остальные обрабатываются универсальным блоком <cite>except</cite> без указания класса. Это значит, что если программа не сможет сконвертировать прочитанные данные из файлов в тип <cite>int</cite>, она выведет сообщение <cite>Неизвестная ошибка</cite>, поскольку эта ситуация выше не обрабатывается (нужно обрабатывать исключение <cite>ValueError</cite>)
			</p>
			<hr/>
			<p>
				Стоит отметить, что хоть обработка исключений - это очень полезный инструмент, "злоупотреблять" им не нужно. То, что можно проверить "вручную", лучше проверять именно так, а не порождать исключения. Классический пример: деление на нуль. Можно легко проверить с помощью if'а, на что будет производиться деление.
			</p><br/>
			<div class="endc">
				<p>
					На этом мы заканчиваем наш вводный курс по программированию на замечательном языке Python. Мы надеемся, что предложенные знания в совокупности с примерами облегчат его дальнейшее изучение в академических и, возможно, профессиональных целях.
				</p>
			</div>
		</div>
		<div class="footer"></div>
	</body>
</html>
